package com.mxsat.common.dao;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlHelper;
import com.mxsat.common.entity.Entity;

public class DaoImpl<M extends BaseMapper<T>, T extends Entity> implements Dao<T> {
	 @Autowired
	    protected M baseMapper;

	    /**
	     * <p>
	     * 鍒ゆ柇鏁版嵁搴撴搷浣滄槸鍚︽垚鍔�
	     * </p>
	     *
	     * @param result 鏁版嵁搴撴搷浣滆繑鍥炲奖鍝嶆潯鏁�
	     * @return boolean
	     */
	    protected boolean retBool(Integer result) {
	        return SqlHelper.retBool(result);
	    }

	    protected Class<T> currentModelClass() {
	        return ReflectionKit.getSuperClassGenericType(getClass(), 1);
	    }

	    /**
	     * <p>
	     * 鎵归噺鎿嶄綔 SqlSession
	     * </p>
	     */
	    protected SqlSession sqlSessionBatch() {
	        return SqlHelper.sqlSessionBatch(currentModelClass());
	    }

	    /**
	     * 閲婃斁sqlSession
	     *
	     * @param sqlSession session
	     */
	    protected void closeSqlSession(SqlSession sqlSession) {
	        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(currentModelClass()));
	    }

	    /**
	     * 鑾峰彇SqlStatement
	     *
	     * @param sqlMethod
	     * @return
	     */
	    protected String sqlStatement(SqlMethod sqlMethod) {
	        return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean save(T entity) {
	        return retBool(baseMapper.insert(entity));
	    }

	    /**
	     * 鎵归噺鎻掑叆
	     *
	     * @param entityList
	     * @param batchSize
	     * @return
	     */
	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean saveBatch(Collection<T> entityList, int batchSize) {
	        int i = 0;
	        String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
	        try (SqlSession batchSqlSession = sqlSessionBatch()) {
	            for (T anEntityList : entityList) {
	                batchSqlSession.insert(sqlStatement, anEntityList);
	                if (i >= 1 && i % batchSize == 0) {
	                    batchSqlSession.flushStatements();
	                }
	                i++;
	            }
	            batchSqlSession.flushStatements();
	        }
	        return true;
	    }

	    /**
	     * <p>
	     * TableId 娉ㄨВ瀛樺湪鏇存柊璁板綍锛屽惁鎻掑叆涓�鏉¤褰�
	     * </p>
	     *
	     * @param entity 瀹炰綋瀵硅薄
	     * @return boolean
	     */
	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean saveOrUpdate(T entity) {
	        if (null != entity) {
	            Class<?> cls = entity.getClass();
	            TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
	            if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
	                Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
	                if (StringUtils.checkValNull(idVal)) {
	                    return save(entity);
	                } else {
	                    /*
	                     * 鏇存柊鎴愬姛鐩存帴杩斿洖锛屽け璐ユ墽琛屾彃鍏ラ�昏緫
	                     */
	                    return updateById(entity) || save(entity);
	                }
	            } else {
	                throw ExceptionUtils.mpe("Error:  Can not execute. Could not find @TableId.");
	            }
	        }
	        return false;
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean saveOrUpdateBatch(Collection<T> entityList) {
	        return saveOrUpdateBatch(entityList, 30);
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
	        if (CollectionUtils.isEmpty(entityList)) {
	            throw new IllegalArgumentException("Error: entityList must not be empty");
	        }
	        Class<?> cls = null;
	        TableInfo tableInfo = null;
	        int i = 0;
	        try (SqlSession batchSqlSession = sqlSessionBatch()) {
	            for (T anEntityList : entityList) {
	                if (i == 0) {
	                    cls = anEntityList.getClass();
	                    tableInfo = TableInfoHelper.getTableInfo(cls);
	                }
	                if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
	                    Object idVal = ReflectionKit.getMethodValue(cls, anEntityList, tableInfo.getKeyProperty());
	                    if (StringUtils.checkValNull(idVal)) {
	                        String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
	                        batchSqlSession.insert(sqlStatement, anEntityList);
	                    } else {
	                        String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
	                        MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
	                        param.put(Constants.ENTITY, anEntityList);
	                        batchSqlSession.update(sqlStatement, param);
	                        //涓嶇煡閬撲互鍚庝細涓嶄細鏈変汉璇存洿鏂板け璐ヤ簡杩樿鎵ц鎻掑叆 馃槀馃槀馃槀
	                    }
	                    if (i >= 1 && i % batchSize == 0) {
	                        batchSqlSession.flushStatements();
	                    }
	                    i++;
	                } else {
	                    throw ExceptionUtils.mpe("Error:  Can not execute. Could not find @TableId.");
	                }
	                batchSqlSession.flushStatements();
	            }
	        }
	        return true;
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean removeById(Serializable id) {
	        return SqlHelper.delBool(baseMapper.deleteById(id));
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean removeByMap(Map<String, Object> columnMap) {
	        if (ObjectUtils.isEmpty(columnMap)) {
	            throw ExceptionUtils.mpe("removeByMap columnMap is empty.");
	        }
	        return SqlHelper.delBool(baseMapper.deleteByMap(columnMap));
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean remove(Wrapper<T> wrapper) {
	        return SqlHelper.delBool(baseMapper.delete(wrapper));
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean removeByIds(Collection<? extends Serializable> idList) {
	        return SqlHelper.delBool(baseMapper.deleteBatchIds(idList));
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean updateById(T entity) {
	        return retBool(baseMapper.updateById(entity));
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean update(T entity, Wrapper<T> updateWrapper) {
	        return retBool(baseMapper.update(entity, updateWrapper));
	    }

	    @Transactional(rollbackFor = Exception.class)
	    @Override
	    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
	        if (CollectionUtils.isEmpty(entityList)) {
	            throw new IllegalArgumentException("Error: entityList must not be empty");
	        }
	        int i = 0;
	        String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
	        try (SqlSession batchSqlSession = sqlSessionBatch()) {
	            for (T anEntityList : entityList) {
	                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
	                param.put(Constants.ENTITY, anEntityList);
	                batchSqlSession.update(sqlStatement, param);
	                if (i >= 1 && i % batchSize == 0) {
	                    batchSqlSession.flushStatements();
	                }
	                i++;
	            }
	            batchSqlSession.flushStatements();
	        }
	        return true;
	    }

	    @Override
	    public T getById(Serializable id) {
	        return baseMapper.selectById(id);
	    }

	    @Override
	    public Collection<T> listByIds(Collection<? extends Serializable> idList) {
	        return baseMapper.selectBatchIds(idList);
	    }

	    @Override
	    public Collection<T> listByMap(Map<String, Object> columnMap) {
	        return baseMapper.selectByMap(columnMap);
	    }

	    @Override
	    public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
	        if (throwEx) {
	            return baseMapper.selectOne(queryWrapper);
	        }
	        return SqlHelper.getObject(baseMapper.selectList(queryWrapper));
	    }

	    @Override
	    public Map<String, Object> getMap(Wrapper<T> queryWrapper) {
	        return SqlHelper.getObject(baseMapper.selectMaps(queryWrapper));
	    }

	    @Override
	    public Object getObj(Wrapper<T> queryWrapper) {
	        return SqlHelper.getObject(baseMapper.selectObjs(queryWrapper));
	    }

	    @Override
	    public int count(Wrapper<T> queryWrapper) {
	        return SqlHelper.retCount(baseMapper.selectCount(queryWrapper));
	    }

	    @Override
	    public List<T> list(Wrapper<T> queryWrapper) {
	        return baseMapper.selectList(queryWrapper);
	    }

	    @Override
	    public IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper) {
	        return baseMapper.selectPage(page, queryWrapper);
	    }

	    @Override
	    public List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
	        return baseMapper.selectMaps(queryWrapper);
	    }

	    @Override
	    public List<Object> listObjs(Wrapper<T> queryWrapper) {
	        return baseMapper.selectObjs(queryWrapper).stream().filter(Objects::nonNull).collect(Collectors.toList());
	    }

	    @Override
	    public IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper) {
	        return baseMapper.selectMapsPage(page, queryWrapper);
	    }
}
